﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="With_ModelBase.Wpf.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.ComponentModel.Modelling
{
    using System.ComponentModel.DataAnnotations;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using TestAssets;

    [TestClass]
    public partial class With_ModelBase
    {
        #region Nested type: When_notify_data_error_info_is_not_supported_and_manual_validation_has_been_implemented

        [TestClass]
        public class When_notify_data_error_info_is_not_supported_and_manual_validation_has_been_implemented
        {
            public const string ValidValue = "ValidValue";

            protected TestModel Model { get; set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Model = new TestModel();
            }

            /// <summary>
            ///   Verifies that when errors are raised against multiple properties within a single property set the appropriate property changed events are raised.
            /// </summary>
            [TestMethod]
            public void If_property_becomes_invalid_should_notify_of_errors()
            {
                Model.ManuallyValidatedValue = ValidValue;
                Model.AssertPropertyChanged.Reset();
                Model.ManuallyValidatedValue = null;
                Model.AssertPropertyChanged.Raised(() => Model.HasErrors, true);
                Model.AssertPropertyChanged.Raised(() => Model.ManuallyValidatedValue);
                Model.AssertPropertyChanged.IsEmpty();
            }

            /// <summary>
            ///   Verifies that when errors are raised against multiple properties within a single property set the appropriate property changed events are raised.
            /// </summary>
            [TestMethod]
            public void If_property_becomes_valid_should_notify_of_errors()
            {
                Model.Validate();
                Model.AssertPropertyChanged.Reset();
                Model.ManuallyValidatedValue = ValidValue;
                Model.AssertPropertyChanged.Raised(() => Model.HasErrors, false);
                Model.AssertPropertyChanged.Raised(() => Model.ManuallyValidatedValue);
                Model.AssertPropertyChanged.IsEmpty();
            }

            /// <summary>
            ///   Verifies that manual and attribute based validation errors raise the appropriate property changed events.
            /// </summary>
            [TestMethod]
            public void If_validated_should_notify_of_errors()
            {
                Model.Validate();
                Model.AssertPropertyChanged.Raised(() => Model.HasErrors, true);
                Model.AssertPropertyChanged.Raised(() => Model.ManuallyValidatedValue);
                Model.AssertPropertyChanged.IsEmpty();
            }

            #region Nested type: TestModel

            public class TestModel : TestModelBase
            {
                #region Fields

                private string _manuallyValidatedValue;

                #endregion

                public string ManuallyValidatedValue
                {
                    get { return _manuallyValidatedValue; }
                    set
                    {
                        // be performing the validation in the changed callback we automatically join the set property suspend scope\
                        SetProperty(ref _manuallyValidatedValue, value, () => ManuallyValidatedValue,
                            (o, n) => ValidateManuallyValidatedValue());
                    }
                }

                private void ValidateManuallyValidatedValue()
                {
                    const string RequiredErrorMessage = "ManuallyValidatedValue is required";
                    if (string.IsNullOrEmpty(ManuallyValidatedValue))
                    {
                        AddError(PropertyNames.ManuallyValidatedValue, RequiredErrorMessage);
                    }
                    else
                    {
                        RemoveError(PropertyNames.ManuallyValidatedValue, RequiredErrorMessage);
                    }
                }

                protected override void OnValidate()
                {
                    ValidateManuallyValidatedValue();
                }

                #region Nested type: PropertyNames

                public class PropertyNames
                {
                    public static readonly string ManuallyValidatedValue = "ManuallyValidatedValue";
                }

                #endregion
            }

            #endregion
        }

        #endregion

        #region Nested type: When_notify_data_error_info_is_not_supported_and_properties_are_adorned_with_validation_attributes

        [TestClass]
        public class When_notify_data_error_info_is_not_supported_and_properties_are_adorned_with_validation_attributes
        {
            protected TestModel Model { get; set; }

            [TestInitialize]
            public void TestInitialize()
            {
                Model = new TestModel();
            }

            [TestMethod]
            public void If_property_becomes_invalid_should_add_error_and_notify()
            {
                // set the required value property to be valid
                Model.RequiredValue = "Valid Value";
                Model.AssertPropertyChanged.Reset();

                // set the required value to null (invalid)
                Model.RequiredValue = null;
                Model.AssertPropertyChanged.Raised(() => Model.HasErrors, true);
                Model.AssertPropertyChanged.Raised(() => Model.RequiredValue, null);
                Model.AssertPropertyChanged.IsEmpty();
            }

            [TestMethod]
            public void If_property_becomes_valid_should_notify()
            {
                // set the required value property to be invalid
                Model.Validate();
                Model.AssertPropertyChanged.Reset();

                // set a valid value and verify notifications
                Model.RequiredValue = "Valid Value";
                Model.AssertPropertyChanged.Raised(() => Model.HasErrors, false);
                Model.AssertPropertyChanged.Raised(() => Model.RequiredValue, "Valid Value");
                Model.AssertPropertyChanged.IsEmpty();
            }

            /// <summary>
            ///   Verifies that manual and attribute based validation errors raise the appropriate property changed events.
            /// </summary>
            [TestMethod]
            public void If_validate_changes_errors_should_notify()
            {
                Model.Validate();
                Model.AssertPropertyChanged.Raised(() => Model.HasErrors, true);
                Model.AssertPropertyChanged.Raised(() => Model.RequiredValue);
                Model.AssertPropertyChanged.IsEmpty();
            }

            [TestMethod]
            public void If_validate_does_not_change_errors_should_not_notify()
            {
                // verify when there is a valid value
                Model.RequiredValue = "Valid Value";
                Model.AssertPropertyChanged.Reset();
                Model.Validate();
                Model.AssertPropertyChanged.IsEmpty();

                // verify when there is an invalid value
                Model.RequiredValue = null;
                Model.AssertPropertyChanged.Reset();
                Model.Validate();
                Model.AssertPropertyChanged.IsEmpty();
            }

            [TestMethod]
            public void If_errors_are_cleared_should_notify()
            {
                Model.Validate();
                Model.AssertPropertyChanged.Reset();
                Model.ClearErrors();
                Model.AssertPropertyChanged.Raised(() => Model.HasErrors, false);
                Model.AssertPropertyChanged.Raised(() => Model.RequiredValue, null);
                Model.AssertPropertyChanged.IsEmpty();
            }

            #region Nested type: TestModel

            public class TestModel : TestModelBase
            {
                #region Field

                private string _requiredValue;

                #endregion

                [Required]
                public string RequiredValue
                {
                    get { return _requiredValue; }
                    set { SetProperty(ref _requiredValue, value, "RequiredValue"); }
                }

                public new void ClearErrors()
                {
                    base.ClearErrors();
                }

                #region Nested type: PropertyNames

                public class PropertyNames
                {
                    public readonly string RequiredValue = "RequiredValue";
                }

                #endregion
            }

            #endregion
        }

        #endregion
    }
}